/**
 * 通用腾讯 COS 存储类
 */
import { Config, Provide, Init } from "@midwayjs/core";
import * as COS from "cos-nodejs-sdk-v5";
import * as STS from "qcloud-cos-sts";
import * as moment from "moment";
import { UploadFileInfo } from "@midwayjs/upload";
import * as fs from "fs";
import * as path from "path";

@Provide()
export class CosService {
  private cos: COS;

  @Config("cosv5")
  private cosConfig: {
    /**
     * 腾讯云 app_id
     */
    app_id: string;
    /**
     * 腾讯云密钥id
     */
    secret_id: string;
    /**
     * 腾讯云密钥
     */
    secret_key: string;
    /**
     * 腾讯云cos存储桶
     */
    bucket: string;
    /**
     * 腾讯云cos存储桶地域
     */
    region: string;
    /**
     * 腾讯云cos存储桶访问域名
     */
    base_url: string;
    /**
     * cos 文件存储根目录，以 / 结尾
     */
    dir_root: string;
  };

  @Config("upload.tmpdir")
  private uploadTmpDir: string;

  get rewardPath() {
    return (
      this.cosConfig.dir_root +
      "reward/" +
      moment(Date.now()).format("YYYYMMDD") +
      "/"
    );
  }

  @Init()
  async init() {
    const policy = {
      version: "2.0",
      statement: [
        {
          action: [
            // 所有 action 请看文档 https://cloud.tencent.com/document/product/436/31923
            // 简单上传
            "name/cos:PutObject",
            "name/cos:PostObject",
            // 分片上传
            "name/cos:InitiateMultipartUpload",
            "name/cos:ListMultipartUploads",
            "name/cos:ListParts",
            "name/cos:UploadPart",
            "name/cos:CompleteMultipartUpload"
          ],
          effect: "allow",
          principal: { qcs: ["*"] },
          resource: [
            "qcs::cos:" +
              this.cosConfig.region +
              ":uid/" +
              this.cosConfig.app_id +
              ":" +
              this.cosConfig.bucket +
              "/*"
          ]
        }
      ]
    };

    this.cos = new COS({
      getAuthorization: (options, callback) => {
        STS.getCredential(
          {
            secretId: this.cosConfig.secret_id,
            secretKey: this.cosConfig.secret_key,
            proxy: "",
            region: this.cosConfig.region,
            policy
          },
          (err, data) => {
            if (err) {
              console.log("获取临时密钥失败");
              console.log(err);
            } else {
              const credentials = data.credentials;
              callback({
                TmpSecretId: credentials.tmpSecretId,
                TmpSecretKey: credentials.tmpSecretKey,
                SecurityToken: credentials.sessionToken,
                StartTime: data.startTime,
                ExpiredTime: data.expiredTime
              });
            }
          }
        );
      }
    });
  }

  private getCosPutResult(
    err: COS.CosSdkError,
    data: COS.PutObjectResult,
    options: any,
    filename: string
  ): CosPutResult {
    if (err) {
      console.log("getCosPutResult.err");
      console.log(err);
      return { success: false, data: null, message: err.message };
    } else {
      if (data.statusCode !== 200) {
        return { success: false, data: null, message: "未知异常" };
      } else {
        return {
          success: true,
          data: {
            cos_url: "https://" + data.Location,
            cos_name: filename,
            cos_key: options.Key
          },
          message: "文件上传成功"
        };
      }
    }
  }

  /**
   * 文件对象上传到腾讯云COS
   * @param file
   * @param savePath 保存路径，不要以斜杠开开始，也不要以斜杠结束。与其他参数结合作为cos的key使用。文件最终存储路径为：cosConfig.dir_root + savePath + fileName
   * @returns
   */
  putFileObject(
    file: UploadFileInfo<any>,
    savePath?: string
  ): Promise<CosPutResult> {
    return new Promise((resolve) => {
      const options: any = {
        Bucket: this.cosConfig.bucket,
        Region: this.cosConfig.region,
        Key: this.rewardPath + file.filename,
        Body: fs.createReadStream(file.data as string),
        ContentLength: fs.statSync(file.data as string).size
      };

      if (file.mimeType) {
        options.ContentType = file.mimeType;
      }

      if (savePath) {
        options.Key = this.cosConfig.dir_root + savePath + "/" + file.filename;
      }

      this.cos.putObject(options, (err, data) => {
        resolve(this.getCosPutResult(err, data, options, file.filename));
      });
    });
  }

  /**
   * 批量上传文件到腾讯云COS
   * @param files 文件列表
   * @param savePath 保存路径，不要以斜杠开开始，也不要以斜杠结束。与其他参数结合作为cos的key使用。文件最终存储路径为：cosConfig.dir_root + savePath + fileName
   * @param sliceSize 设置大于该值的文件采用分块上传，单位Byte，默认10MB
   * @returns
   */
  putFileListObject(
    files: UploadFileInfo<any>[],
    savePath?: string,
    sliceSize: number = 10 * 1024 * 1024
  ): Promise<CosBatchUploadResult> {
    return new Promise((resolve) => {
      const uploadFiles = files.map((file) => {
        const key = savePath
          ? this.cosConfig.dir_root + savePath + "/" + file.filename
          : this.rewardPath + file.filename;

        return {
          Bucket: this.cosConfig.bucket,
          Region: this.cosConfig.region,
          Key: key,
          FilePath: file.data as string,
          ContentType: file.mimeType,
          onTaskReady: (taskId) => {
            console.log("Upload task ready with taskId:", taskId);
          }
        };
      });

      this.cos.uploadFiles(
        {
          files: uploadFiles,
          SliceSize: sliceSize,
          onProgress: (info) => {
            const percent = parseInt((info.percent * 10000).toString()) / 100;
            const speed =
              parseInt(((info.speed / 1024 / 1024) * 100).toString()) / 100;
            console.log("进度：" + percent + "%; 速度：" + speed + "Mb/s;");
          },
          onFileFinish: (err, data, options) => {
            console.log(options.Key + "上传" + (err ? "失败" : "完成"));
          }
        },
        (err, data) => {
          if (err) {
            console.log("批量上传失败:", err);
            resolve({
              success: false,
              data: null,
              message: err.message || "批量上传失败"
            });
            return;
          }

          const failedList = data.files.filter((item) => item.error);

          if (failedList.length > 0) {
            resolve({
              success: false,
              data: {
                successFiles: data.files
                  .filter((item) => !item.error)
                  .map((item) => ({
                    cos_url: "https://" + item.data.Location,
                    cos_name: item.options.Key.split("/").pop(),
                    cos_key: item.options.Key
                  })),
                failedFiles: failedList.map((item) => ({
                  filename: item.options.Key.split("/").pop(),
                  error: item.error
                }))
              },
              message: `部分文件上传失败，共${failedList.length}个`
            });
          } else {
            resolve({
              success: true,
              data: {
                successFiles: data.files.map((item) => ({
                  cos_url: "https://" + item.data.Location,
                  cos_name: item.options.Key.split("/").pop(),
                  cos_key: item.options.Key
                })),
                failedFiles: []
              },
              message: "所有文件上传成功"
            });
          }
        }
      );
    });
  }

  /**
   * 文件流上传到腾讯云COS
   * @param buffer 文件流
   * @param fileName 文件名，包含后缀名
   * @param contentType 文件类型，mime类型
   * @param savePath 保存路径，不要以斜杠开开始，也不要以斜杠结束。与其他参数结合作为cos的key使用。文件最终存储路径为：cosConfig.dir_root + savePath + fileName
   * @returns
   */
  pubBufferObject(
    buffer: Buffer,
    fileName: string,
    contentType: string,
    savePath?: string
  ): Promise<CosPutResult> {
    return new Promise((resolve) => {
      const options: any = {
        Bucket: this.cosConfig.bucket,
        Region: this.cosConfig.region,
        Key: this.rewardPath + fileName,
        // ContentLength: buffer.length,
        Body: buffer
      };
      if (contentType) {
        // options.ContentType = contentType;
      }

      if (savePath) {
        options.Key = this.cosConfig.dir_root + savePath + "/" + fileName;
      }

      this.cos.putObject(options, (err, data) => {
        resolve(this.getCosPutResult(err, data, options, fileName));
      });
    });
  }

  /**
   * 文件流上传到腾讯云COS
   * @param buffer 文件流
   * @param fileName 文件名，包含后缀名
   * @param contentType 文件类型，mime类型
   * @param savePath 保存路径，不要以斜杠开开始，也不要以斜杠结束。与其他参数结合作为cos的key使用。文件最终存储路径为：cosConfig.dir_root + savePath + fileName
   * @returns
   */
  pubStreamObject(
    stream: fs.ReadStream,
    fileName: string,
    contentType: string,
    size: number,
    savePath?: string
  ): Promise<CosPutResult> {
    return new Promise((resolve) => {
      const options: any = {
        Bucket: this.cosConfig.bucket,
        Region: this.cosConfig.region,
        Key: this.rewardPath + fileName,
        Body: stream,
        ContentLength: size
      };
      if (contentType) {
        options.ContentType = contentType;
      }

      if (savePath) {
        options.Key = this.cosConfig.dir_root + savePath + "/" + fileName;
      }

      this.cos.putObject(options, (err, data) => {
        resolve(this.getCosPutResult(err, data, options, fileName));
      });
    });
  }

  /**
   * 批量上传文件流到腾讯云COS
   * @param bufferFiles 文件流对象数组，每个对象包含buffer、fileName和contentType
   * @param savePath 保存路径，不要以斜杠开开始，也不要以斜杠结束。与其他参数结合作为cos的key使用。文件最终存储路径为：cosConfig.dir_root + savePath + fileName
   * @returns
   */
  pubBufferListObject(
    bufferFiles: Array<{
      buffer: Buffer;
      fileName: string;
      contentType?: string;
    }>,
    savePath?: string
  ): Promise<CosBatchUploadResult> {
    return new Promise((resolve) => {
      try {
        // 使用框架配置的临时目录
        const tempDir = this.uploadTmpDir;

        const tempFiles: Array<{
          filePath: string;
          fileName: string;
          contentType?: string;
        }> = [];

        // 将buffer写入临时文件
        for (const file of bufferFiles) {
          const tempFilePath = path.join(
            tempDir,
            `${Date.now()}_${file.fileName}`
          );
          fs.writeFileSync(tempFilePath, file.buffer);
          tempFiles.push({
            filePath: tempFilePath,
            fileName: file.fileName,
            contentType: file.contentType
          });
        }

        // 构建上传文件数组
        const uploadFiles = tempFiles.map((file) => {
          const key = savePath
            ? this.cosConfig.dir_root + savePath + "/" + file.fileName
            : this.rewardPath + file.fileName;

          return {
            Bucket: this.cosConfig.bucket,
            Region: this.cosConfig.region,
            Key: key,
            FilePath: file.filePath,
            ContentType: file.contentType,
            onTaskReady: (taskId) => {
              console.log("Upload task ready with taskId:", taskId);
            }
          };
        });

        // 执行批量上传
        this.cos.uploadFiles(
          {
            files: uploadFiles,
            SliceSize: 10 * 1024 * 1024, // 默认10MB
            onProgress: (info) => {
              const percent = parseInt((info.percent * 10000).toString()) / 100;
              const speed =
                parseInt(((info.speed / 1024 / 1024) * 100).toString()) / 100;
              console.log("进度：" + percent + "%; 速度：" + speed + "Mb/s;");
            },
            onFileFinish: (err, data, options) => {
              console.log(options.Key + "上传" + (err ? "失败" : "完成"));
            }
          },
          (err, data) => {
            // 不需要手动清理临时文件，midwayjs框架会自动清理临时目录

            if (err) {
              console.log("批量上传失败:", err);
              resolve({
                success: false,
                data: null,
                message: err.message || "批量上传失败"
              });
              return;
            }

            const failedList = data.files.filter((item) => item.error);

            if (failedList.length > 0) {
              resolve({
                success: false,
                data: {
                  successFiles: data.files
                    .filter((item) => !item.error)
                    .map((item) => ({
                      cos_url: "https://" + item.data.Location,
                      cos_name: item.options.Key.split("/").pop(),
                      cos_key: item.options.Key
                    })),
                  failedFiles: failedList.map((item) => ({
                    filename: item.options.Key.split("/").pop(),
                    error: item.error
                  }))
                },
                message: `部分文件上传失败，共${failedList.length}个`
              });
            } else {
              resolve({
                success: true,
                data: {
                  successFiles: data.files.map((item) => ({
                    cos_url: "https://" + item.data.Location,
                    cos_name: item.options.Key.split("/").pop(),
                    cos_key: item.options.Key
                  })),
                  failedFiles: []
                },
                message: "所有文件上传成功"
              });
            }
          }
        );
      } catch (error) {
        console.error("批量上传处理异常:", error);
        resolve({
          success: false,
          data: null,
          message: error.message || "批量上传处理异常"
        });
      }
    });
  }
}

export interface CosPutSuccessResult {
  success: true;
  data: {
    /**
     * 文件存储在腾讯云cos的url
     */
    cos_url: string;
    /**
     * 文件在腾讯云cos的存储名称
     */
    cos_name: string;
    /**
     * 文件在腾讯云cos的存储key
     */
    cos_key: string;
  };
  message: string;
}

export interface CosPutFailResult {
  success: false;
  data: null;
  message: string;
}

/**
 * 腾讯云cos上传文件返回结果
 */
export type CosPutResult = CosPutSuccessResult | CosPutFailResult;

export interface CosBatchSuccessResult {
  success: true;
  data: {
    successFiles: {
      cos_url: string;
      cos_name: string;
      cos_key: string;
    }[];
    failedFiles: [];
  };
  message: string;
}

export interface CosBatchFailResult {
  success: false;
  data: {
    successFiles: {
      cos_url: string;
      cos_name: string;
      cos_key: string;
    }[];
    failedFiles: {
      filename: string;
      error: any;
    }[];
  } | null;
  message: string;
}

/**
 * 腾讯云cos批量上传文件返回结果
 */
export type CosBatchUploadResult = CosBatchSuccessResult | CosBatchFailResult;
